package tmt.usercenter.web.controller;

import com.tmt.helper.RandomCodeHelper;
import com.tmt.spring.validation.RegisterValidateHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.WebAsyncTask;
import tmt.usercenter.web.configure.emailservice.IEmailSender;
import tmt.usercenter.web.controller.bean.WorkFlowManager;
import tmt.usercenter.web.controller.bean.WorkFlowNode;
import tmt.usercenter.web.controller.validtor.EmailValidateInfoValidator;
import tmt.usercenter.web.domain.TmtUser;
import tmt.usercenter.web.domain.security.UserDetailsImpl;
import tmt.usercenter.web.service.TmtUserService;
import tmt.usercenter.web.vo.HttpResult;
import tmt.usercenter.web.vo.user.EmailValidateInfo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * 验证电子邮箱流程
 */
@Controller
@RequestMapping("/email")
public class EmailController extends BaseController {

    private static final String EMAIL_VALIDATE_INFO = "_EMAIL_VALIDATE_INFO";

    private static final String EMAIL_VALIDATE_FLOW = "redirect:/email/flow";
    private static final String EMAIL_VALIDATE_PAGE = "redirect:/email/validate";

    private static final List<String> EMAIL_FLOW_NODES = new ArrayList<String>() {{
        add(EMAIL_VALIDATE_FLOW);
        add(EMAIL_VALIDATE_PAGE);
    }};

    @Autowired
    private EmailValidateInfoValidator mviValidator;

    @Autowired
    private TmtUserService userService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private IEmailSender emailSender;

    @InitBinder("validateInfo")
    @Override
    protected void initBinder(WebDataBinder binder) {
        super.initBinder(binder);
        binder.addValidators(mviValidator);
    }

    @RequestMapping(value = "/flow", method = RequestMethod.GET)
    public String validateFlow(HttpServletRequest request, Model model) {
        //如果当前请求不处于当前流程节点上，则直接跳转到当前流程节点对应的请求处。
        String url = WorkFlowManager.assure(request, null);
        if (!StringUtils.isEmpty(url))
            return url;

        //获取当前流程节点。
        WorkFlowNode flowNode = WorkFlowManager.current(request);
        if (flowNode == null  //如果没有绑定工作流，则立即绑定工作流。
                || !WorkFlowManager.containNodes(flowNode, EMAIL_FLOW_NODES)) {
            WorkFlowManager.insertFlowNodes(request, EMAIL_FLOW_NODES);
        }

        return WorkFlowManager.next(request, "redirect:/email/validate");
    }

    @RequestMapping(value = "/validate", method = RequestMethod.GET)
    public String validateEmail(HttpServletRequest request, Model model) {

        //如果当前请求不处于当前流程节点上，则直接跳转到当前流程节点对应的请求处。
        String url = WorkFlowManager.assure(request, null);
        if (!StringUtils.isEmpty(url))
            return url;

        EmailValidateInfo validateInfo = null;
        Object obj = request.getSession().getAttribute(EMAIL_VALIDATE_INFO);
        if (obj == null) {
            String email = "";
            String clientId = "";
            boolean readOnly = true;
            Object user = request.getSession().getAttribute(RegisterController.NEW_USER);
            if (user != null) {
                email = ((TmtUser) user).getEmail();
                readOnly = ((TmtUser) user).isEmailEnabled();
                clientId = ((TmtUser) user).getOriginSystem().getClientId();
            } else {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                if (authentication != null
                        && authentication.getPrincipal() != null
                        && authentication.getPrincipal() instanceof UserDetailsImpl) {
                    email = ((UserDetailsImpl) authentication.getPrincipal()).getEmail();
                    readOnly = ((UserDetailsImpl) authentication.getPrincipal()).isEmailEnabled();
                    clientId = ((UserDetailsImpl) authentication.getPrincipal()).getClientId();
                }
            }
            validateInfo = new EmailValidateInfo();
            validateInfo.setEmail(email);
            validateInfo.setEmailReadOnly(readOnly);
            validateInfo.setClientId(clientId);
            request.getSession().setAttribute(EMAIL_VALIDATE_INFO, validateInfo);
        } else {
            validateInfo = (EmailValidateInfo) obj;
        }

        model.addAttribute("validateInfo", validateInfo);
        return "user/email_validate";
    }

    @RequestMapping(value = "/validate", method = RequestMethod.POST)
    public String validateEmail(HttpServletRequest request,
                                HttpServletResponse response,
                                Model model,
                                @Valid @ModelAttribute("validateInfo") EmailValidateInfo emailInfo,
                                BindingResult result) {
        if (result.hasErrors()) {
            model.addAttribute("validateInfo", emailInfo);
            return "user/email_validate";
        }

        //更新用户的邮箱及其激活状态。
        Object obj = request.getSession().getAttribute(RegisterController.NEW_USER);
        if (obj != null) {
            TmtUser user = (TmtUser) obj;
            user.setEmail(emailInfo.getEmail());
            user.setEmailEnabled(true);
        } else {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null
                    && authentication.getPrincipal() != null
                    && authentication.getPrincipal() instanceof UserDetailsImpl) {
                Long userId = ((UserDetailsImpl) authentication.getPrincipal()).getUserId();
                boolean value = !((UserDetailsImpl) authentication.getPrincipal()).isEmailEnabled();
                userService.updateEmailEnabled(userId, emailInfo.getEmail(), value);
                ((UserDetailsImpl) authentication.getPrincipal()).setEmailEnabled(value);
            }
        }

        request.getSession().removeAttribute(EMAIL_VALIDATE_INFO);

        //进入下一个流程
        return WorkFlowManager.next(request, "user/email_validate_success");
    }

    @ResponseBody
    @RequestMapping(value = "/code", method = RequestMethod.POST)
    public WebAsyncTask<HttpResult> sendEmail(HttpServletRequest request,
                                              @RequestParam("email") String email) {
        //读取Session中保存的EmailValidateInfo对象
        Object obj = request.getSession().getAttribute(EMAIL_VALIDATE_INFO);
        if (obj == null) {
            return new WebAsyncTask<>(() -> HttpResult.failure("访问无效"));
        }

        EmailValidateInfo validateInfo = (EmailValidateInfo) obj;

        //尝试校验电子邮箱
        if (!email.equals(validateInfo.getEmail())) {
            validateInfo.setEmail(email);
            if (!RegisterValidateHelper.validateEmail(email))
                return new WebAsyncTask<>(() -> HttpResult.failure("电子邮箱无效"));
        }

        Callable<HttpResult> callable = () -> {
            String code = RandomCodeHelper.getNumberCode(4);
            validateInfo.setEmail(email);
            validateInfo.setSendCode(passwordEncoder.encode(code));

            //发送验证邮件
            emailSender.sendHtmlEmail(email, "TMT邮箱验证邮件",
                    MessageFormat.format("<div>TMT用户中心发出的邮箱验证码：<span style='color:blue; font-size:30px;'>{0}</span></div>", code));

            return HttpResult.success("验证邮件发送成功", validateInfo);
        };

        return new WebAsyncTask<>(callable);
    }

    /**
     * 预先初始化Session中的EmailValidateInfo对象。
     *
     * @param request
     * @param validateInfo
     */
    public static void initialEmailValidateInfo(HttpServletRequest request,
                                                EmailValidateInfo validateInfo) {
        request.getSession().setAttribute(EMAIL_VALIDATE_INFO, validateInfo);
    }

}
